/***************************************************************************************************
  *Copyright(C),2010-2016,Sumscope
  *FileName	:  FormatTable.cpp
  *Author	:  scofined.qi
  *Version	:  1.0
  *Date		:  2016/08/03
  *Desc		:  //用于主要说明此程序文件完成的主要功能
  *Relation :  
  *Others	:  //其他内容说明
  *Function :  //主要函数列表，每条记录应包含函数名及功能简要说明
  *History	:  //修改历史记录列表，每条修改记录应包含修改日期、修改者及修改内容简介
***************************************************************************************************/
#include "FormatTable.h"
#include "string_util.h"
#include "../system/system_util.h"
#include <vector>
#include <map>
#include <stdlib.h>

namespace qb{
	namespace base{
        typedef std::map<int, BaseString> Record;
        typedef std::map < BaseString, int > IndexMap;
		namespace{
			bool IsRefColumn(const BaseString& name){
				return name.size() > 0 && (name.at(0) == '@'||name.at(0)=='#');
			}
			double ToDouble(Record* precord, int col){
				if (precord)
				{
					Record::iterator it = precord->find(col);
					if (it != precord->end())
						return atof(it->second.c_str());
				}
				return 0.0;
			}
            void UpdateBaseTimes(int i, int  col, std::vector<Record*>& records)
			{
				int nrow = records.size();
				if (nrow < 0)
					return;
				double base = ToDouble(records[0], col);
				for (int k = 0; k < nrow; k++)
				{
					Record* record = records.at(k);
					double d = ToDouble(record, col);
                    (*record)[i] = os::fmtstr("%4.2f", base == 0 ? 0 : (d / base));
				}
			}
            void UpdatePercent(int i,int  col, std::vector<Record*>& records)
			{
				double sum = 0;
				int nrow = records.size();
				for (int k = 0; k < nrow; k++)
				{
					Record* record = records.at(k);
					sum += ToDouble(record, col);
				}
				for (int k = 0; k < nrow; k++)
				{
					Record* record = records.at(k);
                    double d = ToDouble(record, col);
                    (*record)[i] = os::fmtstr("%4.2f%%", sum == 0 ? 0 : (d*100.0 / sum));
				}
			}
		}

#if defined (CCORE_MAC)
		struct sort_r_data
		{
			void* arg;
			int (*compar)(const void* a1, const void* a2, void* aarg);
		};
		int sort_r_arg_swap(void* s, const void* aa, const void* bb)
		{
			struct sort_r_data* ss = (struct sort_r_data*)s;
			return (ss->compar)(aa, bb, ss->arg);
		}
#endif

		class TableColumn{
		public:
			BaseString		name;		// 该列的名称
			int				maxlen;		// 该列的字符串的最大宽度
			int				count;		// 该列被设置了就递增,每当设置了超过1此就换行
		};
		class TableGroup{
			BaseString					m_name;			// 分组名称
            std::vector<TableColumn>	m_columns;		// 列信息
            std::vector<Record*>		m_table;		// 记录行
			IndexMap					m_indexmap;		// 列索引
			BaseString					m_sort_col;		// 
			bool						m_sort_ascend;	// 要排序的列是倒序还是反序
			int							m_compare_col;	// 要排序的列索引
			bool						m_col_isnumber;	// 要排序的列是否全都是数字
			int							m_col_gap;		// 
			bool						m_autolist;		// 
		public:
			TableGroup(const char* name,bool autoList=true) :
				m_autolist(autoList),
				m_sort_ascend(true),
				m_compare_col(0), m_col_isnumber(false),
				m_name(name), m_col_gap(2){
				m_table.push_back(new Record());//新的记录行
			}
			~TableGroup(){
				for (int i = 0; i < (int)m_table.size(); i++)
				{
					Record* precord = m_table.at(i);
					if (precord)
						delete precord;
				}
				m_table.clear();
			}
			const BaseString& GetName()const{ return  m_name; }
			void UpdateRefColumn()
			{
				// 名称以@开头的是引用列,其引用对应名称的列,输出值为占比百分比
				for (int i = 0; i < (int)m_columns.size(); i++)
				{
					TableColumn& tc = m_columns[i];
					if (!IsRefColumn(tc.name))continue;
					BaseString name = tc.name.substr(1, tc.name.size() - 1);
					int col = FindColumn(name);
					if (col < 0)
						continue;

					if (tc.name[0] == '@')
						UpdateBaseTimes(i, col, m_table);
					else if (tc.name[0] == '#')
						UpdatePercent(i, col, m_table);
				}
			}
			void AddField(const BaseString& name, const BaseString& value, int maxLength /* = 0 */)
			{
				bool newline = false;
				int col = GetColumn(name, newline);
				TableColumn& tc = m_columns[col];
				Record* precord = m_table.at(m_table.size() - 1);
				Record& record = *precord;
				if (maxLength > 0 && maxLength < value.size())
				{
					int pos = value.size() - maxLength;
					BaseString cut = "...";
					cut += BaseString(value.c_str() + pos+3, maxLength-3);
					record[col] = cut;
					tc.maxlen = tc.maxlen < cut.size() ? cut.size() : tc.maxlen;
				}
				else
				{
					record[col] = value;
					tc.maxlen = tc.maxlen < value.size() ? value.size() : tc.maxlen;
				}
				if (newline)// 将缺失的列的count补齐,不然会造成后续判断换行不对
				{
					for (int i = 0; i < (int)m_columns.size(); i++)
					{
						m_columns[i].count = 0;
					}
				}
				tc.count++;
			}
			int FindColumn(const BaseString& name)
			{
				IndexMap::iterator it = m_indexmap.find(name);
				if (it != m_indexmap.end())
				{
					int col = it->second;
					if (col >= 0 && col < (int)m_columns.size())
					{
						TableColumn& tc = m_columns[col];
						if (tc.name == name)
							return col;
					}
				}
				return -1;
			}
			int GetColumn(const BaseString& name, bool& newline)
			{
				// 获取列名称name对应的列索引,有可能会增加新列
				// newline是判断是否要增加新记录行
				IndexMap::iterator it = m_indexmap.find(name);
				if (it != m_indexmap.end())
				{
					int col = it->second;
					TableColumn& tc = m_columns[col];
					newline = tc.count >= 1;
					if (newline)
						m_table.push_back(new Record());
					return it->second;
				}

				// 增加新的列
				TableColumn tc;
				tc.name = name;
				tc.maxlen = name.size();
				tc.count = 0;
				int col = m_columns.size();
				m_columns.push_back(tc);
				m_indexmap[name] = col;
				newline = false;
				return col;
			}
			void SortBy(const BaseString& name, bool ascend)
			{
				m_sort_col = name;
				m_sort_ascend = ascend;
			}
			BaseString Format()
			{
				// 将此表格格式化成一个字符串
				AdjustOrder();
				int namewidth=0, valuewidth=0;
				int capcity = CalculateCapcity(namewidth,valuewidth);
				BaseString bs(capcity);
				if (m_table.size() <= 0)
					return bs;
				if (m_autolist && m_table.size() <= 1)
				{
					Record* precord = m_table.at(0);
					for (int i = 0; i < (int)m_columns.size(); i++)
					{
						TableColumn& tc = m_columns.at(i);
						bs.append(tc.name);
						bs.append(namewidth-2 - tc.name.size(), ' ');
						bs.append(": ");
						Record::iterator it = precord->find(i);
						if (it != precord->end())
						{
							BaseString& val = it->second;
							bs.append(val);
							bs.append(valuewidth - val.size(), ' ');
						}
						else
							bs.append(valuewidth, ' ');
						bs.append("\r\n");
					}
					return bs;
				}
				for (int i = 0; i < (int)m_columns.size(); i++)
				{
					TableColumn& tc = m_columns.at(i);
					int colwidth = tc.maxlen + m_col_gap;
					bs.append(tc.name);
					bs.append(colwidth - tc.name.size(), ' ');
				}
				bs.append("\r\n");
				for (int j = 0; j < (int)m_table.size(); j++)
				{
					Record* precord = m_table.at(j);
					for (int i = 0; i < (int)m_columns.size(); i++)
					{
						TableColumn& tc = m_columns.at(i);
						int colwidth = tc.maxlen + m_col_gap;
						Record::iterator it = precord->find(i);
						if (it != precord->end())
						{
							BaseString& val = it->second;
							bs.append(val);
							bs.append(colwidth - val.size(), ' ');
						}
						else
							bs.append(colwidth, ' ');
					}
					bs.append("\r\n");
				}
				return bs;
			}
		protected:
			int CalculateCapcity(int& name_width,int& value_width)
			{
				if (m_table.size() <= 0)
				{
					name_width = 0, value_width = 0;
					return 0;
				}
				if (m_autolist && m_table.size() <= 1)
				{
					Record* precord = m_table.at(0);
					for (int i = 0; i < (int)m_columns.size(); i++)
					{
						TableColumn& tc = m_columns.at(i);
						int namelen = tc.name.size();
						Record::iterator it = precord->find(i);
						int valuelen = it != precord->end() ? it->second.size() : 0;
						name_width = i == 0 ? namelen : (name_width < namelen ? namelen : name_width);
						value_width = i == 0 ? valuelen : (value_width < valuelen ? valuelen : value_width);
					}
					//  2  用于绘制 ": "
					name_width += m_col_gap + 2;
					value_width += m_col_gap;
					int record_size = name_width + value_width  + 1;
					return record_size*m_columns.size();
				}
				int record_size = 0;
				for (int i = 0; i < (int)m_columns.size(); i++)
				{
					TableColumn& tc = m_columns.at(i);
					record_size += tc.maxlen + m_col_gap;
				}
				record_size += 1;
				return record_size*m_table.size() + record_size;
			}
			void AdjustOrder()
			{
				if (m_sort_col.empty())
					return;
				IndexMap::iterator it = m_indexmap.find(m_sort_col);
				if (it == m_indexmap.end())
					return;

				// 根据列col将此排序
				// 判断列col的类型:是数字还是字符串
				m_compare_col = it->second;
				m_col_isnumber = IsColNumber(m_compare_col);
#if defined (WIN32)
				qsort_s(&m_table.at(0), m_table.size(), sizeof(Record*), CompareRecordStatic, this);
#elif defined (CCORE_MAC)
				struct sort_r_data tmp;
				tmp.arg = this;
				tmp.compar = CompareRecordStaticLinux;
				qsort_r(&m_table.at(0), m_table.size(), sizeof(Record*), &tmp, &sort_r_arg_swap);
#else
				qsort_r(&m_table.at(0), m_table.size(), sizeof(Record*), CompareRecordStaticLinux, this);//TODO: MAC maybe different!!
#endif
			}
			bool IsColNumber(int col)
			{
				for (int i = 0; i < (int)m_table.size(); i++)
				{
					Record* precord = m_table.at(i);
					BaseString& val = (*precord)[col];
					if (!StringUtil::IsNumber(val))
						return false;
				}
				return true;
			}
			static int CompareRecordStatic(void * context, const void * re1, const void * re2)
			{
				TableGroup* group = (TableGroup*)context;
				const Record* r1 = (const Record*)re1;
				const Record* r2 = (const Record*)re2;
				int cmp = group->CompareRecord(r1, r2);
				return group->m_sort_ascend ? cmp : (-cmp);
			}
			static int CompareRecordStaticLinux(const void * re1, const void * re2, void * context)
			{
				TableGroup* group = (TableGroup*)context;
				const Record* r1 = (const Record*)re1;
				const Record* r2 = (const Record*)re2;
				int cmp = group->CompareRecord(r1, r2);
				return group->m_sort_ascend ? cmp : (-cmp);
			}
			int CompareRecord(const Record* r1, const Record* r2)
			{
				Record::const_iterator rt1 = r1->find(m_compare_col);
				Record::const_iterator rt2 = r2->find(m_compare_col);
				if (rt1 == r1->end() || rt2 == r2->end())
					return 0;
				const BaseString& s1 = rt1->second;
				const BaseString& s2 = rt2->second;
				if (!m_col_isnumber)
				{
					return s1.compare(s2.c_str(), s2.size());
				}

				double f1 = atof(s1.c_str());
				double f2 = atof(s2.c_str());
				return f1 > f2 ? 1 : (f1 < f2 ? -1 : 0);
			}
		};

        class TableGroupVec : public std::vector < TableGroup* > {};
		FormatTable::FormatTable() :m_autolist(true), m_hasref(false)
		{
			m_groups = new TableGroupVec();
		}

		FormatTable::~FormatTable()
		{
			for (int i = 0; i < (int)m_groups->size();i++)
			{
				TableGroup* group = m_groups->at(i);
				delete group;
			}
			m_groups->clear();
			delete m_groups;
		}

		void FormatTable::AddField(const BaseString& name, const BaseString& value, int maxLength /*= 0*/)
		{
			if (m_groups->size() <= 0)	m_groups->push_back(new TableGroup("",m_autolist));
			TableGroup* group = m_groups->at(m_groups->size() - 1);
			group->AddField(name, value, maxLength);
			if (!m_hasref)
				m_hasref = IsRefColumn(name);
		}

		void FormatTable::AddGroup(const BaseString& name)
		{
			TableGroup* group = new TableGroup(name,m_autolist);
			m_groups->push_back(group);
		}

		void FormatTable::SortBy(const BaseString& name, bool ascend)
		{
			if (m_groups->size() > 0)
			{
				TableGroup* group = m_groups->at(m_groups->size() - 1);
				group->SortBy(name, ascend);
			}
		}
		void FormatTable::UpdateRefColumn()
		{
			if (!m_hasref)
				return;
			for (int i = 0; i < (int)m_groups->size(); i++)
			{
				TableGroup* group = m_groups->at(i);
				group->UpdateRefColumn();
			}
		}
        qb::base::BaseString FormatTable::Format()
		{
			BaseString fmt;
			UpdateRefColumn();
			for (int i = 0; i < (int)m_groups->size(); i++)
			{
				TableGroup* group = m_groups->at(i);
				fmt += group->Format();
			}
			return fmt;
		}
		void FormatTable::FormatToConsole(const char* msg)
		{
			FormatToOutput(kOutputTypeConsole, msg);
		}
		void FormatTable::FormatToLog(const char* msg)
		{
			FormatToOutput(kOutputTypeLog, msg);
		}
		void FormatTable::FormatToOutput(kOutputType type, const char* msg)
		{
			BaseString fmt;
			UpdateRefColumn();
			for (int i = 0; i < (int)m_groups->size(); i++)
			{
				TableGroup* group = m_groups->at(i);
				fmt += group->Format();
			}
			char* buffer = fmt.c_str();
			int len = fmt.size();
			char* start = buffer;
			while (start)
			{
				char* nline = strchr(start, '\n');
				char* next = (nline && nline + 1 < buffer + len) ? (nline + 1) : 0;
				if (nline)	*nline = 0;
				switch (type)
				{
				case kOutputTypeLog:
                //	BLOGINFO("%s%s", msg ? msg : "", start);
					break;
				case kOutputTypeConsole:
					printf("\n%s%s", msg ? msg : "", start);
					break;
				case kOutputTypeDebugView:
                //	qb::base::DebugView(0, 0, 0, "%s%s", msg ? msg : "", start);
					break;
				}
				start = next;
			}
		}
		void FormatTable::FormatToDebugView(const char* msg)
		{
			FormatToOutput(kOutputTypeDebugView, msg);
		}

	}//namespace base;
}//namespace ss;
